package proxyc

import (
	"gitee.com/yysf-xin/dproxy/pkg/app"
	"gitee.com/yysf-xin/dproxy/pkg/db"
	"gitee.com/yysf-xin/dproxy/pkg/model"
	"gitee.com/yysf-xin/dproxy/pkg/rest"
	"gitee.com/yysf-xin/dproxy/pkg/svr"
	"gitee.com/yysf-xin/dproxy/pkg/tools"
	"github.com/go-chi/chi/v5"
	"golang.org/x/net/proxy"
	"net/http"
	"strconv"
	"sync"
)

type SSHControl struct {
	DB         *db.Client
	Dialer     proxy.Dialer
	service    *app.Services
	sshMap     map[uint]*svr.SshClient
	sshMapLock sync.RWMutex
}

func NewSSHControl(DB *db.Client, service *app.Services, dialer proxy.Dialer) *SSHControl {
	t := &SSHControl{DB: DB, Dialer: dialer, service: service, sshMap: make(map[uint]*svr.SshClient)}
	t.Init()
	return t
}

func (d *SSHControl) String() string {
	return "CONTROL@SSH"
}

func (d *SSHControl) Init() {
	if d.DB == nil {
		return
	}
	var svrList db.SshServerList
	svrList.ListByEnabled(d.DB.Db)
	for _, v := range svrList {
		rsa, err := d.DB.Cipher.DecryptBase64(v.IdRsa)
		if err != nil {
			tools.Log.Println("error rsa", rsa)
			continue
		}
		v.IdRsa = string(rsa)

		cli, err := d.newSSHClient(v)
		if err == nil {
			d.sshMap[v.Id] = cli
			go cli.Start()
		} else {
			tools.Log.Println("ssh client", v.Remote, err)
		}
	}
}

func (d *SSHControl) Destroy() {

}

func (d *SSHControl) InitRoute(router chi.Router) {

	router.Get("/", app.Json(nil, d.list))
	router.Get("/{id}", app.JsonHttp(nil, d.ById))
	router.Route("/{id}/", func(r chi.Router) {
		r.Post("/enable", app.JsonHttp(func() interface{} {
			return &model.State{}
		}, d.handlerEnable))
	})

	router.Delete("/{id}", app.JsonHttp(nil, d.Delete))

}

func (d *SSHControl) handlerEnable(w http.ResponseWriter, r *http.Request, i interface{}) (rs interface{}, err error) {
	id, err := rest.PathParamId(r)
	if err != nil {
		return
	}
	var p db.SshServer
	err = d.DB.Db.First(&p, id).Error
	if err != nil {
		return
	}

	rsa, err := d.DB.Cipher.DecryptBase64(p.IdRsa)
	p.IdRsa = string(rsa)
	if err != nil {
		return
	}
	if i.(*model.State).Status == 1 {
		err = d.startSSH(&p)
	} else {
		d.stopSSH(&p)
	}
	return

}

func (d *SSHControl) newSSHClient(s *db.SshServer) (cli *svr.SshClient, err error) {
	cli, err = svr.NewSshClient(s)
	if err == nil {
		cli.Dialer = d.Dialer
		d.service.AddService(cli)
	}
	return
}

func (d *SSHControl) Delete(_ http.ResponseWriter, r *http.Request, i interface{}) (rs interface{}, err error) {
	id, err := strconv.ParseUint(chi.URLParam(r, "id"), 10, 64)
	if err != nil {
		return
	}
	if err = d.DB.Db.Delete(&d.DB.Models.SshServer, id).Error; err != nil {
		return
	}
	d.sshMapLock.RLock()
	c := d.sshMap[uint(id)]
	d.sshMapLock.RUnlock()

	if c != nil {
		d.stopSSH(c.Cfg)
	}

	return
}

func (d *SSHControl) startSSH(p *db.SshServer) (err error) {
	d.stopSSH(p)
	c, err := d.newSSHClient(p)
	if err == nil {
		d.sshMapLock.Lock()
		d.sshMap[p.Id] = c
		d.sshMapLock.Unlock()
		err = c.Start()
	}
	return
}

func (d *SSHControl) stopSSH(p *db.SshServer) {
	d.sshMapLock.RLock()
	c := d.sshMap[p.Id]
	d.sshMapLock.RUnlock()
	if c == nil {
		return
	}
	c.Stop()
	d.sshMapLock.Lock()
	delete(d.sshMap, p.Id)
	d.sshMapLock.Unlock()
	d.service.RemoveService(c)
}

func (d *SSHControl) list(i interface{}) (rs interface{}, err error) {
	var list []*db.SshServer
	if err = d.DB.Db.Find(&list).Error; err != nil {
		return
	}
	for _, v := range list {
		v.IdRsa = ""
	}
	rs = &list
	return
}

func (d *SSHControl) ById(w http.ResponseWriter, r *http.Request, i interface{}) (rs interface{}, err error) {
	id := chi.URLParam(r, "id")
	var ss db.SshServer
	err = d.DB.Db.First(&ss, id).Error
	if err == nil {
		ss.IdRsa = ""
		rs = &ss
	}
	return
}
